﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Data.SqlClient;
using System.Web.Services.Protocols;
using System.Configuration;
using DataModels;

[WebService(Namespace = "http://localhost/MainService")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
[System.Web.Script.Services.ScriptService]

public class Service : System.Web.Services.WebService
{
    public Service () {

        //Uncomment the following line if using designed components 
        //InitializeComponent(); 
    }

    #region Party Hosting methods
    /*---------------------------------------------------------------------------------*\
     * Methods used by the desktop application so the host can control the party.
     * The user needs to be authenticated for all of these.
     * 
     * Iteration 1 Goals for Party Control:
     *  - Create a party (with no settings options)
     *  - Add/remove songs to/from playlist
     *  - Get the next song in the queue (this updates the queue)
     *  - Update the Now Playing song
    \*---------------------------------------------------------------------------------*/

    // The following two fields will most likely be removed once sessions are deprecated
    private string AuthenticatedID
    {
        get { return (string)Session["AuthenticatedHostID"]; }
        set { Session["AuthenticatedHostID"] = value; }
    }

    private string HostPartyID
    {
        get { return (string)Session["HostPartyID"]; }
        set { Session["HostPartyID"] = value; }
    }

    /// <summary>
    /// Begins a new session on the server and authenticates the user as a party host.
    /// </summary>
    /// <returns>returns true if the user is authenticated successfully</returns>
    [WebMethod(EnableSession=true)]
    public bool BeginHostSession(string facebookId)
    {        
        //Start a new session if there already was one.
        if (!Session.IsNewSession)
            Session.Clear();

        //TODO:
        //  1. Authenticate a signed request using our app_secret
        //  2. Get the userId from the facebookSignedRequest instead of as a string argument
        //      (This can be done using the .NET Facebook API implementation.)

        AuthenticatedID = facebookId;
        return true;
    }



    /// <summary>
    /// Creates a party in the database and returns the party code.
    /// Requires that the user has authenticated.
    /// 
    /// TODO: Party privacy and settings. Future Iteration
    /// </summary>
    /// <returns>
    /// returns the new party's partyid the application should use to identify it
    /// with theserver and the party access code that party host will give to his
    /// or her guests.
    /// </returns>
    [WebMethod(EnableSession = true)]
    public CreatePartyData CreateParty(string description, string accessCode, int accessLevel, int votesPerUser, int voteReplenish, bool allowsRequests)
    {
        // TODO: Authenticate session data
        //RequireAuthentication();

        Guid partyID = Guid.NewGuid();

        //TODO: take this in as a parameter or make random
        string partyAccessCode = accessCode;

        try
        {
            SqlConnection myConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["NextrackConn"].ConnectionString);
            myConnection.Open();

            //Create party
            using (SqlCommand command = new SqlCommand("INSERT INTO Parties VALUES(@ID, @Created_Time,"+
                            "@End_Time, @Description, @Party_Owner, @Access_Code, @Access_Level,"+
                            " @Votes_Per_User, @Vote_Replenish, @Allows_Requests, @Now_Playing)", myConnection))
            {
                command.Parameters.Add(new SqlParameter("ID", partyID.ToString()));
                command.Parameters.Add(new SqlParameter("Created_Time", DateTime.Now));
                command.Parameters.Add(new SqlParameter("End_Time", DateTime.Now.AddHours(24)));
                command.Parameters.Add(new SqlParameter("Description", description));
                command.Parameters.Add(new SqlParameter("Party_Owner", "fake_user_id")); //TODO: add real user ID
                command.Parameters.Add(new SqlParameter("Access_Code", partyAccessCode));
                command.Parameters.Add(new SqlParameter("Access_Level", accessLevel)); 
                command.Parameters.Add(new SqlParameter("Votes_Per_User", votesPerUser)); 
                command.Parameters.Add(new SqlParameter("Vote_Replenish", voteReplenish)); 
                command.Parameters.Add(new SqlParameter("Allows_Requests", allowsRequests));
                command.Parameters.Add(new SqlParameter("Now_Playing", String.Empty)); 
                command.ExecuteNonQuery();
            }
            
            //Consider creating long queue row...

            //TODO: Create permissions

            myConnection.Close();

            // save the newly created party id in the session variable
            HostPartyID = partyID.ToString();

            return new CreatePartyData() { PartyID = partyID.ToString(), AccessCode = partyAccessCode };
        }
        catch (Exception ex) 
        {
            System.Diagnostics.Debug.WriteLine(ex);
            return new CreatePartyData();
        }
    }

    /// <summary>
    /// Add a song to this party's playlist by adding it to Songs
    /// Requires a host session
    /// </summary>
    /// <param name="artist">Artist</param>
    /// <param name="album">Album</param>
    /// <param name="title">Song title</param>
    /// <param name="partyID">Party ID to add song to</param>
    /// <returns>ID of newly created song</returns>
    [WebMethod(EnableSession = true)]
    public string AddSong(string partyID, string artist, string album, string title)
    {
        //Generate song guid
        Guid songID = Guid.NewGuid();

        try
        {
            SqlConnection myConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["NextrackConn"].ConnectionString);
            myConnection.Open();

            //Add song
            using (SqlCommand command = new SqlCommand("INSERT INTO Songs VALUES(@Party_ID, @Song_ID," +
                            "@Title, @Artist, @Album, @Up_Vote_Stat, @Down_Vote_Stat, @Last_Played)", myConnection))
            {
                command.Parameters.Add(new SqlParameter("Party_ID", "fake_party_id"));
                command.Parameters.Add(new SqlParameter("Song_ID", songID.ToString()));
                command.Parameters.Add(new SqlParameter("Title", title));
                command.Parameters.Add(new SqlParameter("Artist", artist));
                command.Parameters.Add(new SqlParameter("Album", album));
                command.Parameters.Add(new SqlParameter("Up_Vote_Stat", 1));
                command.Parameters.Add(new SqlParameter("Down_Vote_Stat", 1));
                //Need to pass parameter, and so we know it has not been played, make it in the future
                command.Parameters.Add(new SqlParameter("Last_Played", DateTime.Now.AddYears(1)));
                command.ExecuteNonQuery();

                //Return new ID if success
                return songID.ToString();
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex);
            //Return empty string upon failure
            return String.Empty;
        }
    }

    /// <summary>
    /// Remove a song from the song library and queues
    /// </summary>
    /// <param name="partyID">Current party ID</param>
    /// <param name="songID">Song to remove</param>
    /// <returns>Returns true if successful</returns>
    [WebMethod(EnableSession = true)]
    public bool RemoveSong(string partyID, string songID)
    {
        try
        {
            SqlConnection myConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["NextrackConn"].ConnectionString);
            myConnection.Open();

            //Delete song
            using (SqlCommand command = new SqlCommand("DELETE FROM Songs WHERE Song_ID=@Song_ID AND Party_ID=@Party_ID", myConnection))
            {
                command.Parameters.Add(new SqlParameter("Song_ID", songID));
                command.Parameters.Add(new SqlParameter("Party_ID", partyID));
                command.ExecuteNonQuery();
            }

            //Delete song from queues
            using (SqlCommand command = new SqlCommand("DELETE FROM Queues WHERE Song_ID=@Song_ID AND Party_ID=@Party_ID", myConnection))
            {
                command.Parameters.Add(new SqlParameter("Song_ID", songID));
                command.Parameters.Add(new SqlParameter("Party_ID", partyID));
                command.ExecuteNonQuery();
            }

            return true;
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex);
            return false;
        }
    }

    /// <summary>
    /// Called by the desktop to update the now-playing song
    /// </summary>
    /// <param name="songID">Song ID to set now playing</param>
    [WebMethod(EnableSession=true)]
    public bool UpdateNowPlaying(string partyID, string songID)
    {
        //Set now playing song to new song
        try
        {
            SqlConnection myConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["NextrackConn"].ConnectionString);
            myConnection.Open();

            //Create party
            using (SqlCommand command = new SqlCommand("UPDATE Parties SET Now_Playing=@Now_Playing WHERE ID=@ID", myConnection))
            {
                command.Parameters.Add(new SqlParameter("Now_Playing", songID));
                command.Parameters.Add(new SqlParameter("ID", partyID));
                command.ExecuteNonQuery();
            }

            return true;
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex);
            return false;
        }

        //TODO: Check to see if song was in the queue


        //TODO: Song was in the queue - remove from queue and update

        
        //Song was not in queue - do nothing for now

    }

    /// <summary>
    /// If there is no session variable for the user's id or for the party id
    /// that this host is currently controlling, it will throw a soap exception
    /// 
    /// soap exception is sent to the client.
    /// </summary>
    private void RequireHostSession()
    {
        RequireAuthentication();

        if (HostPartyID == null)
            throw new SoapException("The user is not an authenticated party host.",
                new System.Xml.XmlQualifiedName("NexTrackSessionException"));
    }

    /// <summary>
    /// If there is no session variable for the user's id,
    /// throws a SoapException with the NexTrackSessionException code.
    /// 
    /// A soap exception is sent to the client.
    /// </summary>
    private void RequireAuthentication()
    {
        if (AuthenticatedID == null)
            throw new SoapException("The user is not authenticated.",
                new System.Xml.XmlQualifiedName("NexTrackSessionException"));
    }

    //---------------------------------------------------------------------------------//
    #endregion


    #region Data Retrieval Methods
    /*---------------------------------------------------------------------------------*\
     * Methods used by the desktop application and also the mobile application
     * No authentication is necessary.
    \*---------------------------------------------------------------------------------*/

    /// <summary>
    /// Call to upvote a song
    /// </summary>
    /// <param name="partyID">Current party ID</param>
    /// <param name="songID">Song to upvote</param>
    /// <returns>Does queue need to be updated</returns>
    [WebMethod]
    public bool UpvoteSong(string partyID, string songID)
    {
        //this will be implemented fully in the next iteration

        //Update song vote count

        //Get total song vote count

        //Get queue, if song not in queue run helper to see if queue needs to be updated
        //look into using stored procs for this

        throw new NotImplementedException();
    }

    /// <summary>
    /// Call to downvote song
    /// </summary>
    /// <param name="partyID">Current party ID</param>
    /// <param name="songID">Song ID to downvote</param>
    /// <returns>Does queue need to be updated</returns>
    [WebMethod]
    public bool DownvoteSong(string partyID, string songID)
    {
        //this will be implemented fully in the next iteration

        //Update song vote count

        //Get queue, check to see if song is in queue

        //If song is in queue, run helper to see if queue needs to be changed
        //look into using stored procs for this

        throw new NotImplementedException();
    }

    /// <summary>
    /// Returns the current data of the song that is playing
    /// </summary>
    /// <param name="partyID">ID of the party</param>
    /// <returns>Song data of the song currently playing</returns>
    [WebMethod]
    public SongData GetNowPlaying(string partyID)
    {
        SqlDataReader reader;

        try
        {
            SqlConnection myConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["NextrackConn"].ConnectionString);
            myConnection.Open();

            //Get ID
            using (SqlCommand command = new SqlCommand("SELECT * FROM Parties, Songs Where Parties.ID=@ID AND Songs.Song_ID=Parties.Now_Playing", myConnection))
            {
                command.Parameters.Add(new SqlParameter("ID", partyID));
                reader = command.ExecuteReader();

                reader.Read();
                return new SongData() {
                                            SongID = reader["Now_Playing"].ToString(),
                                            Artist = reader["Artist"].ToString(),
                                            Album = reader["Album"].ToString(),
                                            Title = reader["Title"].ToString()
                                        };
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex);
            return new SongData();
        }
    }

    /// <summary>
    /// Gets queue data for a given party
    /// </summary>
    /// <param name="partyID">ID of party</param>
    /// <returns>List of queue data</returns>
    [WebMethod]
    public List<QueueData> GetQueue(string partyID)
    {
        List<QueueData> PartyQueue = new List<QueueData>();
        SqlDataReader reader;

        try
        {
            SqlConnection myConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["NextrackConn"].ConnectionString);
            myConnection.Open();

            //Create party
            using (SqlCommand command = new SqlCommand("SELECT * FROM Queues, Songs WHERE Queues.Party_ID=@Party_ID AND Queues.Song_ID=Songs.Song_ID", myConnection))
            {
                command.Parameters.Add(new SqlParameter("Party_ID", partyID));
                reader = command.ExecuteReader();
            }

            while (reader.Read())
            {
                PartyQueue.Add(new QueueData() 
                                    {
                                        Song = new SongData()
                                        {
                                            Title = reader["Title"].ToString(),
                                            Artist = reader["Artist"].ToString(),
                                            Album = reader["Album"].ToString(),
                                            SongID = reader["Song_ID"].ToString()
                                        },
                                        UpVotes = Int32.Parse(reader["Up_Votes"].ToString()),
                                        DownVotes = Int32.Parse(reader["Down_Votes"].ToString())
                                    });
            }

            //load the queue items from the database into the PartyQueue list
            //return the list in sorted order
            //TODO: sort, pending discussion
            return PartyQueue;
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex);
            return null;
        }
    }

    #endregion
}
